module Widgets
  class Base    
    attr_accessor :name, :object, :url, :partial
        
    def initialize(name, options = {})
      @name = name
      @partial = options[:partial] || self.class.name.underscore
      
      @html = options[:html] || {}
      @html[:id] ||= name.to_s.underscore << '_menu'
      @html[:class] ||= @html[:id]

      if @controller = options[:controller]
        @object = Handle.wrap options[:object], @controller
        @url = Handle.wrap options[:url], @controller
      end
    end
    
    def object
      @object.is_a?(Widgets::Handle) ? @object.call : @object
    end
    
    def url
      @url.is_a?(Widgets::Handle) ? @url.call(object) : @url
    end
    
    def build!
    end
  end
  
  class Menu < Base
    include Highlightable
    include Disableable
    
    attr_accessor :items
    
    def initialize(name, options = {})      
      super name, options
      @items = []
    end
    
    def add_item(name, options = {})
      @items << Item.new(name, options)
      items.last
    end
    
    class Item < Menu
      def initialize(name, options = {})
        super
        @html[:title] = options[:title] 
      end      
    end
  end

  class TreeMenu < Menu
    def build!
      add_item object.title, :url => @url, :object => object, :controller => @controller
      build_items(self, object.children)
    end
    
    def build_items(menu, nodes)
      nodes.each do |child| 
        submenu = menu.add_item child.title, :url => @url, :object => child, :controller => @controller
        build_items(submenu, child.children)
      end
    end
  end
  
  private

  class Handle
    def self.wrap(subject, controller)
      subject.is_a?(Proc) || subject.is_a?(Symbol) ? Handle.new(subject, controller) : subject
    end
    
    def initialize(subject, controller)
      @subject, @controller = subject, controller
    end
  
    def call(*args)
      if @subject.is_a? Symbol
        @controller.send @subject, *args
      elsif @subject.is_a? Proc
        lambda{ |*args| @subject.call(@controller, *args) }.call(*args)
      else
        @subject
      end
    end
  end  
end
